﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LexLib.LexemNameSpace;
using System.Reflection;

namespace TextEditor4.Parser
{
    class Parser
    {
        public Parser(List<Lexem> ArrOfLexems)
        {
            arrOfLexems = ArrOfLexems;

        }
        List<Lexem> arrOfLexems;

        int numFigureBraket = 0;
        int numNormalBraket = 0;
        private int tmpFinalPos = 0;
        string[] badLexems = new string[3]{"LexLib.LexemNameSpace.Modificator",
                                           "LexLib.LexemNameSpace.SyntaxType",
                                           "LexLib.LexemNameSpace.ConditionType"};
        private int tmpFinalPos2 = 0;
        /// <summary>
        /// номер теукущей лексемы
        /// </summary>
        private int numCurrentLexem = 0;

        public IParseNodeType parse()
        {
            try
            {
                IParseNodeType tmp = (parse(0, (new SeqenseType(0, "Program itself"))));
                if ((numFigureBraket == numNormalBraket) && (numNormalBraket == 0) && (tmpFinalPos == arrOfLexems[arrOfLexems.Count - 1].endIndex))
                {
                    return tmp;
                }
                else if (!((numFigureBraket == numNormalBraket) && (numNormalBraket == 0)))
                {
                    throw (new ParseException(0, tmpFinalPos, "Incorrect number of brakets"));
                }
                else
                {
                    throw (new ParseException(tmpFinalPos2, arrOfLexems[arrOfLexems.Count - 1].endIndex, "Incorrect after-program symbols"));
                }
            }
            catch (Exception ex)
            {
                if (ex is ParseException)
                {
                    throw ex;
                }
                else
                {
                    throw (new ParseException(0, tmpFinalPos, "Error during work of parser"));
                }
            }
        }
        //MethodInfo myMethodInfoForAddChild = (new ExpressionType(new Lexem(new ProgramType(), 0, 0))).GetType().GetMethod("addChild");
        MethodInfo myMethodInfoForAddChild = (new SeqenseType(0)).GetType().GetMethod("addChild");

        /// <summary>
        /// парсилка
        /// Будем считать, что встретить переменную в парсилке можно только в том случае, 
        /// если она или ошибочна(что плохо, и мы пока не будем это рассматривать), 
        /// или этой переменной что либо присваивается. Все остальные случаи хочу рассматривать вложенными методами
        /// </summary>
        /// <param name="arrOfLexems">лист Лексем для парсинья</param>
        public IParseNodeType parse(int index, IParseNodeType node)
        {
            ExpressionType myExpression = new ExpressionType(arrOfLexems[index]);
            tmpFinalPos = myExpression.endIndex;
            if ((arrOfLexems[index].TypeOfLexem is ProgramType) && (arrOfLexems[index + 1].TypeOfLexem is OpenFigureBracketType))
            {
                myExpression = new ExpressionType(arrOfLexems[index + 1]);
                numFigureBraket++;
                myMethodInfoForAddChild.Invoke(node, makeArray(myExpression));
                //сдвинули глобальный указатель
                numCurrentLexem += 2;
                while (numFigureBraket != 0)
                {
                    IParseNodeType tmp = parse(numCurrentLexem, node);
                    //ужно проверить, не нужно ли стереть предыдущего сына(в случае мат.Операций)
                    if (badLexems.Contains(tmp.name))
                    {
                        node.listOfChildren.RemoveAt(node.listOfChildren.Count - 1);
                    }
                    tmpFinalPos2 = tmp.endIndex;
                    myMethodInfoForAddChild.Invoke(node, makeArray(tmp));
                }

                return node;
            }
            ///если есть переменная
            else if ((arrOfLexems[index].TypeOfLexem is VarNameType) ||
                (arrOfLexems[index].TypeOfLexem is ConstType) ||
                (arrOfLexems[index].TypeOfLexem is EndOfLineType) ||
                (arrOfLexems[index].TypeOfLexem is OtherKeyWord)||
                (arrOfLexems[index].TypeOfLexem is Comment))
            {
                numCurrentLexem++;
                return myExpression;
            }
            //while
            else if (arrOfLexems[index].TypeOfLexem is While)
            {
                try
                {
                    SeqenseType whileCondition = new SeqenseType(arrOfLexems[index + 1].beginIndex, "Conditions for 'While'");
                    whileCondition.addChild(new ExpressionType(arrOfLexems[index + 1]));
                    int tmpNumNormalBrakets = numNormalBraket;
                    int tmpPosFirstBraket = arrOfLexems[index + 1].beginIndex;
                    numNormalBraket++;
                    numCurrentLexem += 2;
                    while (numNormalBraket != tmpNumNormalBrakets)
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, whileCondition);
                        if (badLexems.Contains(tmp.name))
                        {
                            whileCondition.listOfChildren.RemoveAt(whileCondition.listOfChildren.Count - 1);
                        }
                        whileCondition.addChild(tmp);
                    }
                    if (whileCondition.listOfChildren.Count <= 2)
                    {
                        throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "Enter condition for 'while' statement"));
                    }
                    else if (whileCondition.listOfChildren.Count > 3)
                    {
                        throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "You can write only one condition for 'while' statement"));
                    }
                    else if (!whileCondition.listOfChildren[1].name.Equals("LexLib.LexemNameSpace.ConditionType"))
                    {
                        throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "You write " + whileCondition.listOfChildren[1].name + "in 'while' condition instead of boolean expression"));
                    }
                    SeqenseType whileBody = new SeqenseType(arrOfLexems[numCurrentLexem].beginIndex, "Body of cycle 'while'");
                    whileBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    tmpPosFirstBraket = arrOfLexems[numCurrentLexem].BeginIndex;
                    int tmpNumFigureBrakets = numFigureBraket;
                    numFigureBraket++;
                    numCurrentLexem++;
                    while (tmpNumFigureBrakets != numFigureBraket)
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, whileBody);
                        if (badLexems.Contains(tmp.name))
                        {
                            whileBody.listOfChildren.RemoveAt(whileBody.listOfChildren.Count - 1);
                        }
                        whileBody.addChild(tmp);
                    }
                    if (whileBody.listOfChildren.Count <= 2)
                    {
                        throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "Enter body for 'while' statement"));
                    }


                    myExpression.addChild(whileCondition);
                    myExpression.addChild(whileBody);
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in a cycle 'While'"));
                    }
                }

            }
            ///описание функции
            else if (arrOfLexems[index].TypeOfLexem is Function)
            {
                try
                {
                    //добавили имя функции
                    if (!(arrOfLexems[index + 1].TypeOfLexem is VarNameType))
                    {
                        throw (new ParseException(myExpression.begIndex, myExpression.endIndex, "Write function name"));
                    }
                    ExpressionType tmpDef = new ExpressionType(arrOfLexems[index + 1]);
                    tmpDef.name = "LexLib.LexemNameSpace.TypeDefinition";
                    tmpDef.addChild(new ExpressionType(arrOfLexems[index + 1]));
                    myExpression.addChild(tmpDef);
                    numCurrentLexem += 3;
                    //после прибавления указываем на скобочку
                    SeqenseType functionArgs = new SeqenseType(arrOfLexems[index + 1].beginIndex, "Arguments for function");
                    int tmpPosFirstBraket = arrOfLexems[index + 2].beginIndex;
                    int tmpNumNormalBrakets = numNormalBraket;
                    numNormalBraket++;
                    functionArgs.addChild(new ExpressionType(arrOfLexems[index + 2]));
                    while (tmpNumNormalBrakets != numNormalBraket)
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, functionArgs);
                        /*if (badLexems.Contains(tmp.name))
                        {
                           functionArgs.listOfChildren.RemoveAt(functionArgs.listOfChildren.Count - 1);
                        }*/
                        if ((!tmp.name.Equals("LexLib.LexemNameSpace.TypeDefinition")) && (!tmp.name.Equals("LexLib.LexemNameSpace.EndOfLineType")) && (!tmp.name.Equals("LexLib.LexemNameSpace.CloseNormalBracketType")))
                        {
                            throw (new ParseException(tmp.begIndex, tmp.endIndex, "Invalid function arguments"));
                        }
                        functionArgs.addChild(tmp);

                    }
                    SeqenseType functionBody = new SeqenseType(arrOfLexems[index + 1].beginIndex, "Body of function");
                    int tmpNumFigureBrakets = numFigureBraket;
                    numFigureBraket++;
                    //добавили скобочку
                    functionBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    while (tmpNumFigureBrakets != numFigureBraket)
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, functionBody);
                        if (badLexems.Contains(tmp.name))
                        {
                            functionBody.listOfChildren.RemoveAt(functionBody.listOfChildren.Count - 1);
                        }
                        functionBody.addChild(tmp);
                    }
                    myExpression.addChild(functionArgs);
                    myExpression.addChild(functionBody);
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in function"));
                    }
                }
            }
            ///}
            else if (arrOfLexems[index].TypeOfLexem is CloseFigureBracketType)
            {
                numCurrentLexem++;
                numFigureBraket--;
                return myExpression;
            }
            ///)
            else if (arrOfLexems[index].TypeOfLexem is CloseNormalBracketType)
            {
                numCurrentLexem++;
                numNormalBraket--;
                return myExpression;
            }
            ///определение типа
            else if (arrOfLexems[index].TypeOfLexem is TypeDefinition)
            {
                try
                {
                    numCurrentLexem++;
                    myExpression.name = "LexLib.LexemNameSpace.TypeDefinition";
                    myExpression.addChild(parse(numCurrentLexem, myExpression));
                    while ((!(arrOfLexems[numCurrentLexem].TypeOfLexem is EndOfLineType)) &&
                           (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseNormalBracketType)))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, myExpression);
                        if (badLexems.Contains(tmp.name))
                        {
                            myExpression.listOfChildren.RemoveAt(myExpression.listOfChildren.Count - 1);
                        }
                        myExpression.addChild(tmp);

                    }
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in 'TypeDefinition'"));
                    }
                }
            }
            ///+,=,-,*,/, условия
            else if ((arrOfLexems[index].TypeOfLexem is SyntaxType) ||
                (arrOfLexems[index].TypeOfLexem is Modificator) ||
                (arrOfLexems[index].TypeOfLexem is ConditionType))
            {
                try
                {
                    numCurrentLexem++;
                    myExpression.addChild(node.listOfChildren[node.listOfChildren.Count - 1]);
                    while ((!(arrOfLexems[numCurrentLexem].TypeOfLexem is EndOfLineType)) &&
                           (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseNormalBracketType)))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, myExpression);
                        if (badLexems.Contains(tmp.name))
                        {
                            myExpression.listOfChildren.RemoveAt(myExpression.listOfChildren.Count - 1);
                        }
                        myExpression.addChild(tmp);
                    }
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in 'Syntax'"));
                    }
                }
            }
            ///for(){}
            else if (arrOfLexems[index].TypeOfLexem is For)
            {
                try
                {
                    numCurrentLexem += 2;
                    //ExpressionType forExpression = new ExpressionType(arrOfLexems[index]);
                    ///добавили скобочку
                    //forExpression.addChild(new ExpressionType(arrOfLexems[index + 1]));
                    ///прочитали условия
                    SeqenseType forCondition = new SeqenseType(arrOfLexems[index + 1].BeginIndex, "Condition for cycle 'For'");
                    int tmpPosFirstBraket = arrOfLexems[index + 1].beginIndex;
                    forCondition.addChild(new ExpressionType(arrOfLexems[index + 1]));
                    while (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseNormalBracketType))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, forCondition);
                        if (badLexems.Contains(tmp.name))
                        {
                            forCondition.listOfChildren.RemoveAt(forCondition.listOfChildren.Count - 1);
                        }
                        forCondition.addChild(tmp);
                    }
                    //forExpression.addChild(forCondition);
                    forCondition.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    if (!(forCondition.listOfChildren[0].name.Equals("LexLib.LexemNameSpace.OpenNormalBracketType") &&
                        (forCondition.listOfChildren[2].name.Equals("LexLib.LexemNameSpace.EndOfLineType")) &&
                        (forCondition.listOfChildren[3].name.Equals("LexLib.LexemNameSpace.ConditionType")) &&
                        (forCondition.listOfChildren[4].name.Equals("LexLib.LexemNameSpace.EndOfLineType")) &&
                        (forCondition.listOfChildren[5].name.Equals("LexLib.LexemNameSpace.Modificator")))
                        )
                    {
                        if ((!(forCondition.listOfChildren[1].name.Equals("LexLib.LexemNameSpace.TypeDefinition"))) &&
                            (!(forCondition.listOfChildren[1].name.Equals("LexLib.LexemNameSpace.Modificator"))))
                        {
                            throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "Problem in 'for' var definition"));
                        }
                        else
                        {
                            throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "Problem in 'for' statement's condition"));
                        }
                    }
                    else if ((!(forCondition.listOfChildren[1].name.Equals("LexLib.LexemNameSpace.TypeDefinition"))) &&
                            (!(forCondition.listOfChildren[1].name.Equals("LexLib.LexemNameSpace.Modificator"))))
                    {
                        throw (new ParseException(tmpPosFirstBraket, tmpFinalPos, "Problem in 'for' var definition"));
                    }
                    //forExpression.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    //после увеличения указывает на открывающую
                    numCurrentLexem++;
                    //forExpression.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    //numCurrentLexem++;
                    SeqenseType forBody = new SeqenseType(arrOfLexems[numCurrentLexem].BeginIndex, "Body of cycle 'For'");
                    forBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    while (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseFigureBracketType))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, forBody);
                        if (badLexems.Contains(tmp.name))
                        {
                            forBody.listOfChildren.RemoveAt(forBody.listOfChildren.Count - 1);
                        }
                        forBody.addChild(tmp);
                    }
                    //forExpression.addChild(forBody);
                    //forExpression.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    forBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    myExpression.addChild(forCondition);
                    myExpression.addChild(forBody);
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in a cycle 'For'"));
                    }
                }

            }
            ///для описания скобочек, где НЕ последовательность сиквенсов
            else if (arrOfLexems[index].TypeOfLexem is OpenNormalBracketType)
            {
                try
                {
                     ExpressionType interBrakets = new ExpressionType(new Lexem(new InterNormalBraketsType(), arrOfLexems[index].beginIndex, 0));
                    interBrakets.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    int tmpNumOfNormBrakets = numNormalBraket;
                    numNormalBraket++;
                    while (tmpNumOfNormBrakets != numNormalBraket)
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, interBrakets);
                        if (badLexems.Contains(tmp.name))
                        {
                            interBrakets.listOfChildren.RemoveAt(interBrakets.listOfChildren.Count - 1);
                        }
                        interBrakets.addChild(tmp);
                    }
                    return interBrakets;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in NormalBrakets"));
                    }
                }
            }
            ///if(){}else{}
            else if (arrOfLexems[index].TypeOfLexem is If)
            {
                try
                {
                     numCurrentLexem++;
                    ///добавили условие

                    myExpression.addChild(parse(numCurrentLexem, myExpression));
                     //после увеличения указывает на открывающую
                    SeqenseType ifBody = new SeqenseType(arrOfLexems[numCurrentLexem].BeginIndex, "Body of 'If'");
                    ifBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    while (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseFigureBracketType))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, ifBody);
                        if (badLexems.Contains(tmp.name))
                        {
                            ifBody.listOfChildren.RemoveAt(ifBody.listOfChildren.Count - 1);
                        }

                        ifBody.addChild(tmp);
                    }
                    ifBody.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                    numCurrentLexem++;
                    //myExpression.addChild(ifCondition);
                    myExpression.addChild(ifBody);
                    if (arrOfLexems[numCurrentLexem].TypeOfLexem is Else)
                    {
                        SeqenseType elseExpr = new SeqenseType(arrOfLexems[numCurrentLexem].beginIndex, "Body of 'Else'");
                        numCurrentLexem++;
                        //добавили открывающую скобочку
                        elseExpr.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                        numCurrentLexem++;
                        while (!(arrOfLexems[numCurrentLexem].TypeOfLexem is CloseFigureBracketType))
                        {
                            IParseNodeType tmp = parse(numCurrentLexem, elseExpr);
                            if (badLexems.Contains(tmp.name))
                            {
                                elseExpr.listOfChildren.RemoveAt(elseExpr.listOfChildren.Count - 1);
                            }
                            elseExpr.addChild(tmp);
                        }
                        elseExpr.addChild(new ExpressionType(arrOfLexems[numCurrentLexem]));
                        myExpression.addChild(elseExpr);
                        numCurrentLexem++;
                    }

                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in 'If'"));
                    }
                }
            }
            else if (arrOfLexems[index].TypeOfLexem is Return)
            {
                try
                {
                    numCurrentLexem++;
                    while (!(arrOfLexems[numCurrentLexem].TypeOfLexem is EndOfLineType))
                    {
                        IParseNodeType tmp = parse(numCurrentLexem, myExpression);
                        if (badLexems.Contains(tmp.name))
                        {
                            myExpression.listOfChildren.RemoveAt(myExpression.listOfChildren.Count - 1);
                        }
                        myExpression.addChild(tmp);
                    }
                    return myExpression;
                }
                catch (Exception ex)
                {
                    if (ex is ParseException)
                    {
                        throw ex;
                    }
                    else
                    {
                        throw (new ParseException(myExpression.begIndex, tmpFinalPos, "Problem in 'return'"));
                    }
                }

            }
            else
            {
                throw new ParseException(myExpression.begIndex, tmpFinalPos, "Can'myt identify a lexeme");
                //return myExpression;
            }
        }

        private object[] makeArray(IParseNodeType node)
        {
            object[] o = new object[1];
            o[0] = node;
            return o;
        }
    }
}
